//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#include "general.h"

#include <buola/io/cfolder.h>
#include <buola/io/filesystem.h>

#include "sql_conf.h"

CConfServer::CConfServer()
	:	dbus::CObject<CConfServer>(dbus::nTree,"/org/buola/BigBrother/Conf")
{
    InitDatabase();
}

CConfServer::~CConfServer()
{
}

void CConfServer::InitDatabase()
{
    mDB.Create("sqlite");

    OpenDatabase();
    PrepareQueries();
}

void CConfServer::OpenDatabase()
{
    using namespace sql_conf;

    io::CURI lBaseURL=io::get_home()/".buola"/"bb";
    
    io::CFile lBaseFile(lBaseURL);
    if(!io::exists(lBaseURL))
        io::mkdir(lBaseURL,true);
    
    io::CURI lURI=lBaseURL/"conf.db";
    
    bool lExisted=io::exists(lURI);

    mDB.SetURI(lURI);
    mDB.Connect();

    mDB.Exec(sPragmas);

    if(!lExisted)
    {
        mDB.Exec(sCreateTables);
        mDB.Exec(sDefaultData);
    }
}

void CConfServer::PrepareQueries()
{
    using namespace sql_conf;

    mQueries.mInsertFolder.Create(mDB,sInsertFolder);
    mQueries.mInsertKey.Create(mDB,sInsertKey);
    mQueries.mGetFolderID.Create(mDB,sGetFolderID);
    mQueries.mGetChildren.Create(mDB,sGetChildren);
    mQueries.mGetKey.Create(mDB,sGetKey);
    mQueries.mGetKeyNames.Create(mDB,sGetKeyNames);
    mQueries.mGetKeys.Create(mDB,sGetKeys);
}

int CConfServer::GetFolderID(const std::string &pFolder,bool pCreate)
{
    using namespace sql_conf;

    {
        CDBQueryRun lQ(mQueries.mGetFolderID);

        lQ.BindAll(pFolder);
        lQ.Exec();

        if(lQ.NextRow())
            return lQ.Column<int>(0);
    }

    if(pCreate)
        return CreateFolder(pFolder);
    else
        return -1;
}

int CConfServer::CreateFolder(const std::string &pFolder)
{
    using namespace sql_conf;

    std::size_t lSlash=pFolder.rfind('/');

    if(lSlash==std::string::npos)
        throw XArgument("malformed config folder");

    int lParentID;

    if(lSlash==0)
        lParentID=GetFolderID("/");
    else
        lParentID=GetFolderID(pFolder.substr(0,lSlash),true);

    CDBQueryRun lQ(mQueries.mInsertFolder);

    if(lSlash+1>=pFolder.length())
        throw XArgument("malformed config folder");

    lQ.Bind(sInsertFolder_Path,pFolder);
    lQ.Bind(sInsertFolder_Parent,lParentID);

    lQ.Exec();

    return lQ.GetLastInsertID();
}

UValue CConfServer::GetValue(const dbus::CPath &pPath,const std::string &pName)
{
    using namespace sql_conf;

    int lFolderID=GetFolderID(pPath.GetRelative(this));

    if(lFolderID<0)
        return UValue();

    CDBQueryRun lQ(mQueries.mGetKey);

    lQ.Bind(sGetKey_Folder,lFolderID);
    lQ.Bind(sGetKey_Name,pName);

    lQ.Exec();

    if(lQ.NextRow())
    {
        return UValue(lQ.Column<std::string>(1),lQ.Column<std::string>(2));
    }
    else
    {
        return UValue();
    }
}

std::vector<std::string> CConfServer::GetKeyList(const dbus::CPath &pPath)
{
    using namespace sql_conf;

    int lFolderID=GetFolderID(pPath.GetRelative(this));

    std::vector<std::string> lList;

    if(lFolderID<0) return lList;

    CDBQueryRun lQ(mQueries.mGetKeyNames);

    lQ.BindAll(lFolderID);

    lQ.Exec();

    while(lQ.NextRow())
    {
        lList.push_back(lQ.Column<std::string>(0));
    }

    return lList;
}

std::map<std::string,UValue> CConfServer::GetKeyValueList(const dbus::CPath &pPath)
{
    using namespace sql_conf;

    int lFolderID=GetFolderID(pPath.GetRelative(this));

    std::map<std::string,UValue> lMap;

    if(lFolderID<0) return lMap;

    CDBQueryRun lQ(mQueries.mGetKeys);

    lQ.BindAll(lFolderID);

    lQ.Exec();

    while(lQ.NextRow())
        lMap[lQ.Column<std::string>(0)]=UValue(lQ.Column<std::string>(1),lQ.Column<std::string>(2));

    return lMap;
}

void CConfServer::SetValue(const dbus::CPath &pPath,const std::string &pKey,const UValue &pValue)
{
    using namespace sql_conf;

    int lFolderID=GetFolderID(pPath.GetRelative(this),true);

    CDBQueryRun lQ(mQueries.mInsertKey);

    lQ.Bind(sInsertKey_Folder,lFolderID);
    lQ.Bind(sInsertKey_Name,pKey);
    lQ.Bind(sInsertKey_DataType,pValue.Type());
    lQ.Bind(sInsertKey_DataValue,pValue.Value());

    lQ.Exec();
}
